package org.jawp.persistence;

import java.net.UnknownHostException;
import java.util.LinkedList;
import java.util.List;
import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
import com.mongodb.Mongo;
import com.mongodb.MongoException;

public class CategoryDAO {
	static private CategoryDAO dao = null;
	private final DBCollection categoryCollection;
	private final ProductDAO productDAO;
	private int currentId;

	/*************************************
	 *			Constructor
	 * @throws MongoException 
	 * @throws UnknownHostException 
	 *************************************/

	private CategoryDAO(ProductDAO productDAO) throws UnknownHostException, MongoException { //TODO change to private, add a Singleton constructor
		// connect to mongoDB (ip and port number are optional)
		Mongo mongo = new Mongo();

		// get database from MongoDB,
		// if database doesn't exists, mongoDB will create it automatically
		DB db = mongo.getDB("JAWP_DB");

		// Get collection from MongoDB, database named "Categories"
		// if collection doesn't exists, mongoDB will create it automatically
		categoryCollection = db.getCollection("Categories");

		// query the lastest id
		DBCursor cursor = categoryCollection.find().sort(new BasicDBObject("id", -1)).limit(1);
		// get the result
		if(cursor.size() == 0)
			currentId = 0;
		else{
			DBObject result = cursor.next();
			currentId = ((Integer)result.get("id")).intValue() + 1;
		}
		
		this.productDAO = productDAO;
	}

	public static CategoryDAO getCategoryDAO(ProductDAO productDAO) throws UnknownHostException, MongoException {
		if(dao == null){
			dao = new CategoryDAO(productDAO);
		}
		return dao;
	}

	/*************************************
	 *				Creation
	 *************************************/

	public Category createNewCategory(String name, int motherCategoryId) {
		Category category = getCategoryByNameAndMotherCategory(name, motherCategoryId);
		if(category == null){
			category = new Category(currentId, name, motherCategoryId);

			// create a document to store key and value
			BasicDBObject document = new BasicDBObject();
			document.put("id", currentId);
			document.put("name", name);
			document.put("motherCategoryId", motherCategoryId);

			// save it into collection
			categoryCollection.insert(document);

			// increment current id
			currentId++;
		}
		return category;
	}
	
	public Category createNewCategory(String name) {
		return createNewCategory(name, -1);
	}

	/*************************************
	 *				Getter
	 *************************************/

	public List<Category> getCategories() {		 
		// search query
		BasicDBObject searchQuery = new BasicDBObject();

		// query it
		DBCursor cursor = categoryCollection.find(searchQuery);
		
		List<Category> categorys = new LinkedList<Category>();
		while(cursor.hasNext()){
			DBObject object = cursor.next();
			Integer motherCategory = (Integer)object.get("motherCategoryId");
			categorys.add(new Category((Integer)object.get("id"), (String)object.get("name"), motherCategory==null?-1:motherCategory ));
		}
		
		return categorys;
	}
	
	public List<Category> getCategoriesByMotherId(int motherCategoryId) {		 
		// search query
		BasicDBObject searchQuery = new BasicDBObject();

		// query it
		DBCursor cursor = categoryCollection.find(searchQuery);
		searchQuery.put("motherCategoryId", motherCategoryId);
		
		List<Category> categorys = new LinkedList<Category>();
		while(cursor.hasNext()){
			DBObject object = cursor.next();
			categorys.add(new Category((Integer)object.get("id"), (String)object.get("name"), (Integer)object.get("motherCategoryId") ));
		}
		
		return categorys;
	}
	
	public List<Category> getCategoriesByMotherId() {		 
		return getCategoriesByMotherId(-1);
	}

	public Category getCategoriesById(int id) {		 
		// search query
		BasicDBObject searchQuery = new BasicDBObject();
		searchQuery.put("id", id);

		// query it
		DBCursor cursor = categoryCollection.find(searchQuery);

		if(cursor.size() == 0){
			return null;
		}

		DBObject object = cursor.next();
		return new Category((Integer)object.get("id"), (String)object.get("name"), (Integer)object.get("motherCategoryId") );
	}

	public Category getCategoryByNameAndMotherCategory(String name, int categoryId) {		 
		// search query
		BasicDBObject searchQuery = new BasicDBObject();
		searchQuery.put("name", name);
		searchQuery.put("motherCategoryId", categoryId);

		// query it
				DBObject object = categoryCollection.findOne(searchQuery);

				if(object == null){
					return null;
				}

		return new Category((Integer)object.get("id"), (String)object.get("name"), (Integer)object.get("motherCategoryId") );
	}

	public Category getCategoryByNameAndMotherCategory(String name) {		 
		return getCategoryByNameAndMotherCategory(name, -1);
	}
	
	public String getCategoryTreeStructure(Category category){
		StringBuilder sb = new StringBuilder();
		if(category.getMotherCategory() != -1){
			sb.append(getCategoryTreeStructure(getCategoriesById(category.getMotherCategory())));
			sb.append(" > ");
		}
		sb.append(category.getName());
		return sb.toString();
	}

	/*************************************
	 *				Setter
	 *************************************/

	/*************************************
	 *				Deletion
	 *************************************/

	public void deleteCategory(int id) {

		BasicDBObject searchQuery = new BasicDBObject();
		searchQuery.put("id", id);

		categoryCollection.remove(categoryCollection.findOne(searchQuery));
		
		//TODO : delete associated products in this method or not ? > imply to have a reference on productDAO (only used here)
		// delete associated products
		productDAO.deleteProductsByCategory(id);
	}
}
